import glob
import sys
import subprocess
import os
import platform
from pathlib import Path
from fnmatch import fnmatch
import time

objdump_path = "objdump"
addr2line_path = "addr2line"


def recalculate_offset(line):
    if '/' not in line:
        return line.split('(')[0].split()[-1], -1
    lib_name = line.split('/')[-1].split('(')[0].strip()
    offset = line.split('/')[0].split('pc ')[-1].strip()
    lib_path = find_so(lib_name)
    if lib_path == None:
        print("Failed to find " + lib_name)
        return "", -1
    lib_path = Path(lib_path)
    if lib_path.is_file():
        return lib_name, offset
    elif lib_name == '':
        print("lib_name is empty")
    else:
        print("Failed to find " + lib_name)
    return "", -1


def find_so(lib_name):
    paths = glob.glob(f"{os.getcwd()}\\**\\**\\**\\{lib_name}") \
            + glob.glob(f"{os.getcwd()}\\**\\**\\{lib_name}") \
            + glob.glob(f"{os.getcwd()}\\**\\{lib_name}")
    if len(paths) >= 1:
        return paths[0]
    else:
        return None


def read_addr2line_result(lib_name, offset):
    lib_path = find_so(lib_name)
    # print(lib_path)
    if offset == -1:
        return ""
    # print(lib_name, offset)
    # return subprocess.Popen(addr2line_path + " -Cfpie " + lib_path + " " + offset, shell=True, stdout=subprocess.PIPE).stdout.read()
    result = os.popen(f"{addr2line_path} -Cfpie {lib_path} {offset}").read()
    return result


def get_so_ctime(lib_name):
    lib_path = find_so(lib_name)
    filemtime = float(os.path.getmtime(lib_path))
    f_mtime = time.strftime('%Y-%m-%d-%H-%M-%S', time.localtime(filemtime))
    return f_mtime


def process_crash_file(log_path):
    print("Current Log:" + log_path)
    file_path = os.path.dirname(log_path)
    file_name = "processd_" + log_path.split('\\')[-1]
    ret_log_name = os.path.join(file_path, file_name)
    out_file = open(ret_log_name, "w")
    with open(log_path) as log:
        for line in log:
            out_file.write(line)
            if line.startswith('#'):
                lib_name, offset = recalculate_offset(line)
                if file_name != '':
                    write_line = read_addr2line_result(lib_name, offset)
                    if len(write_line) != 0:
                        if lib_name != '':
                            f_mtime = get_so_ctime(lib_name)
                            out_file.write(str(f_mtime) + '\n')
                        out_file.write("Origin:" + offset + " " + str(write_line) + "\n")
    out_file.close()


def is_cpp_crash_file(file_name):
    if fnmatch(file_name, 'cppcrash*'):
        return True
    return False


def is_appfreeze_file(file_name):
    if fnmatch(file_name, 'appfreeze*'):
        return True
    return False


def process_crash_file_dir(log_path):
    for file_name in os.listdir(log_path):
        file_path = os.path.join(log_path, file_name)
        # if not os.path.isfile(file_path) or not is_cpp_crash_file(file_name):
        if os.path.isfile(file_path) and (is_cpp_crash_file(file_name) or is_appfreeze_file(file_name)):
            process_crash_file(file_path)


def main(argv):
    if len(argv) != 2:
        print("usage: put log and unstripped-binary in current folder and run addr2line.py [log-name].\n")
        return
    print("current os is ", platform.platform())
    log_path = argv[1]
    if not os.path.isdir(log_path):
        process_crash_file(log_path)
        return
    process_crash_file_dir(log_path)


if __name__ == "__main__":
    main(sys.argv)
